# -*- encoding:utf-8 -*-
# __author__ = 'chenyongbing'
# __poject__ = 'ops_platform'
# __datetime__ = '2022/6/13'
# __name__ = 'gitlab_helper.py'
import gitlab
import re
# import datetime
from django.utils import timezone as datetime
import logging
# Create your code here.
logger = logging.getLogger()


class GL():
    def __init__(self, url, token):
        self.gl = gitlab.Gitlab(url=url, private_token=token)
    def get_users(self):
        users = {}
        for user in self.gl.users.list(all=True):
            users[user.username] = user.name.strip()
            users[user.email] = user.name.strip()
        return users

    def get_commit_parent_stat(self, project, datas={}, parents=[], merge_date=''):
        for parent in parents:
            commit = project.commits.get(parent)
            title = commit.title
            if re.search('Merge branch', title) or re.search('Merge remote-tracking', title):
                datas = self.get_commit_parent_stat(project, datas, parents=commit.parent_ids, merge_date=committed_date)
                continue

        return datas

    def _commit_stat(self, project, ref, commit, datas={}, merge_date=None):
        commitid = commit.id
        if commitid in datas.keys(): return datas
        title = commit.title
        committed_date = datetime.datetime.strptime(commit.committed_date, '%Y-%m-%dT%H:%M:%S.%fZ').replace(
            tzinfo=None) + datetime.timedelta(hours=8)
        if re.search('Merge branch', title) or re.search('Merge remote-tracking', title):
            merge_date = merge_date or committed_date
            parent_ids = commit.parent_ids
            for parent_id in parent_ids[1:]:
                parent = project.commits.get(parent_id)
                datas = self._commit_stat(project, ref, parent, datas, merge_date=merge_date)
        else:

            shortcommitid = commit.short_id
            committer_name = commit.committer_name.lower()
            committer_email = commit.committer_email.lower()

            committer = self.users.get(committer_email, self.users.get(committer_name, committer_name))

            data = commit.stats
            data.update({
                'commitid': commitid,
                'shortcommitid': shortcommitid,
                'committer': committer,
                'committer_email': committer_email,
                'committed_date': committed_date,
                'ref': ref if ref else 'unknown',
                'projectname': project.name,
                'merge_date': merge_date
            })
            datas[commitid] = data
        return datas

    def get_all_commit_stat(self, refs=[], since=None, until=None, blacklist = []):
        self.users = self.get_users()
        datas = {}
        for project in self.gl.projects.list(all=True):
            if project.name in blacklist: continue
            if refs == []: refs = [None]
            for ref in refs:
                commits = project.commits.list(ref_name=ref, since=since, until=until, with_stats=True, all=True)
                
                for commit in commits:
                    datas = self._commit_stat(project, ref, commit, datas)
                    # print(project.name, ref, commit)
                    # title = commit.title
                    # committed_date = datetime.datetime.strptime(commit.committed_date, '%Y-%m-%dT%H:%M:%S.%fZ').replace(
                    #     tzinfo=None) + datetime.timedelta(hours=8)
                    # if re.search('Merge branch', title) or re.search('Merge remote-tracking', title):
                    #     self.get_commit_parent_stat(project, datas, parents=commit.parent_ids, merge_date= committed_date)
                    #     continue
                    # commitid = commit.id
                    # if commitid in datas.keys():continue
                    # shortcommitid = commit.short_id
                    # committer_name = commit.committer_name.lower()
                    # committer_email = commit.committer_email.lower()
                    #
                    # committer = self.users.get(committer_email, self.users.get(committer_name, committer_name))
                    #
                    # data = commit.stats
                    # data.update({
                    #     'commitid': commitid,
                    #     'shortcommitid': shortcommitid,
                    #     'committer': committer,
                    #     'committer_email': committer_email,
                    #     'committed_date': committed_date,
                    #     'ref': ref if ref else 'unknown',
                    #     'projectname': project.name
                    # })
                    # datas[commitid] = data
        return datas

    def _get_project_by_name(self, project_name_with_namespace):
        '''
        通过 project_name_with_namespace 获取project
        :param project_name_with_namespace:
        :return:
        '''
        project = self.gl.projects.get(project_name_with_namespace)
        return project

    def _get_project_branch(self, project, branch_name):
        '''
        获取branch
        :param project:
        :param branch_name:
        :return:
        '''
        try:
            branch = project.branches.get(branch_name)
            return branch
        except:
            return None

    def _get_project_commit(self, project, commit):
        '''
        获取commit
        :param project:
        :param commit:
        :return:
        '''
        try:
            commit = project.commits.get(commit)
            return commit
        except:
            return None

    def _check_project_commit_branch(self, commit, branch_name):
        '''
        判断commit是否提交至branch
        :param commit:
        :param branch:
        :return:
        '''
        if branch_name in [ref.get('name') for ref in commit.refs('branch')]:
            return True
        else:
            return False

    def _get_project_tag(self, project, tag_name):
        '''

        :param project:
        :param tag_name:
        :return:
        '''
        try:
            tag = project.tags.get(tag_name)
            return tag
        except Exception as err:
            logger.error(err)
            return False
    def _create_tag(self, project, ref, tag_name):
        '''
        创建Tag
        :param project:
        :param ref:
        :param tag_name:
        :return:
        '''
        tag = project.tags.create({'ref': ref, 'tag_name': tag_name})
        return tag

    def _delete_branch(self, project, branch_name):
        '''
        删除分支
        :param project:
        :param branch_name:
        :return:
        '''
        try:
            project.branches.delete(branch_name)
            return True
        except Exception as err:
            return err
    def _safe_delete_branch(self, project, branch_name, tag_name):
        '''
        安全删除分支
        :param project:
        :param branch_name:
        :param tag_name:
        :return:
        '''
        self._create_tag(project, ref=branch_name, tag_name='{}_before_{}'.format(branch_name, tag_name))


    def _create_branch(self, project, ref, branch_name):
        '''
        创建分支
        :param project:
        :param from_commit:
        :param branch_name:
        :return:
        '''
        try:
            branch = project.branches.create({'ref': ref, 'branch': branch_name})
            return branch
        except:
            return False

    def replace_branch(self, project_name_with_namespace, version='', target='online'):
        '''
        1. 判断分支<target>是否存在
        2. 判断ref是否存在于<target>
        3. 判断ref是否为最新
        4. 创建tag用于备份<target>
        5. 删除<target>
        6. 从ref创建<target>
        :param project_name_with_namespace:
        :param ref:
        :param target:
        :return:
        '''
        vlist = version.split('_')
        ref = '_'.join(version.split('_')[:len(vlist) - 3])
        shortcommit = version.split('_')[len(vlist) - 3]
        tag_name = ref + '_' + shortcommit + '_' + datetime.datetime.today().strftime('%Y%m%d')
        project = self._get_project_by_name(project_name_with_namespace)
        commit = self._get_project_commit(project, shortcommit)
        if not commit:
            errmsg = "项目: {}, CommitID {} 不存在,无法继续替换动作".format(project_name_with_namespace, shortcommit)
            logger.error(errmsg)
            return False, errmsg
        branch_in_commit = self._check_project_commit_branch(commit, target)
        if branch_in_commit:
            msg = "项目: {}, CommitID {} 已存在于分支 {}, 无需替换".format(project_name_with_namespace, shortcommit, target)
            logger.info(msg)
            return True, msg

        branch = self._get_project_branch(project, target)
        if not branch:
            errmsg = "项目: {}, 分支: {}  不存在, 可直接替换".format(project_name_with_namespace, target)
            logger.warning(errmsg)
        else:
            bak_tag_name = '{}_brfore_{}'.format(target, tag_name)
            tag = self._get_project_tag(project, bak_tag_name)
            if not tag:
                tag = self._create_tag(project, ref=target, tag_name=bak_tag_name)
            else:
                errmsg = "项目: {}, Tag: {} 已存在，无需重复创建".format(project_name_with_namespace, bak_tag_name)
                logger.info(errmsg)
            if not tag:
                errmsg = "项目: {}, 源分支: {}, 目标Tag: {} 创建失败".format(project_name_with_namespace, target, bak_tag_name)
                logger.error(errmsg)
                return False, errmsg
            ret = self._delete_branch(project, branch_name=target)
            if ret != True:
                errmsg = "项目: {}, 分支: {} 删除失败".format(project_name_with_namespace, target)
                logger.error(errmsg)
                return False, errmsg
        newbranch = self._create_branch(project, shortcommit, target)
        if not newbranch:
            errmsg = "项目: {}, 源: {}, 目标分支: {} 替换失败".format(project_name_with_namespace, shortcommit, target)
            logger.error(errmsg)
            return False, errmsg
        msg = '项目: {}, 源: {}, 目标分支: {} 替换成功'.format(project_name_with_namespace, shortcommit, target)
        logger.info(msg)
        return True, msg



if __name__ == '__main__':
    import sys
    url = sys.argv[1]
    token = sys.argv[2]

    logging.basicConfig(stream=sys.stdout, level=logging.INFO)

    gl = GL(url=url, token=token)

    print(gl.get_all_commit_stat(refs=[], since='2022-07-06 00:00:00', until='2022-07-06 23:59:59'))
    version = "t1_61ccc1a_2022222_123"
    version = "t1_3a606c74_20220222_123"
    # version = "t1_cac57ceb_20220222_123"
    # gl.replace_branch(project_name_with_namespace='chenyongbing/testhook', version=version, target='online3')
